/**
 * Copyright © 2014 TingerSure. All rights reserved.
 *
 * This file and the file named SComet.js are the same except there are some
 * notes.
 * 
 * Use step:<br />
 * 
 * 1. Get a object SComet by the global variable SComet.Such as:
 *
 * 			var conn = SComet.getSComet();
 * 
 * 2. If you have changed the config utl-tail in file scomet.properties, you must do this.
 * 
 * 			conn.tail = ...a string;
 * 
 * 3. Set the call back function. Such as:
 * 
 * 			conn.callBack = function( message ){
 * 				...some code;
 * 			};
 * 
 * 4. Link to the Server. Such as:
 * 
 * 			conn.link();
 * 
 * 5. Then, you can do anything with the message in the function callBack.
 * 
 * 6. And, you can also send some messages to the Server. Such as:
 * 
 *  		conn.send("Hello World!");
 *  
 */

/**
 * This is the only one global variable in this javascript file. It should be
 * used to obtain an object SComet by the function getScomet.
 */
var SComet = new function() {
	/**
	 * This is the function which is always used to make a new object SComet.
	 */
	this.getSComet = function() {
		/**
		 * Return a object SComet from an anonymous function.
		 */
		return new function() {
			/**
			 * This is the status of the object.<br>
			 * 0 means linked off.<br>
			 * 1 means linked on.
			 */
			this.status = 0;
			/**
			 * The id of the connection. It comes from the Server by connection
			 * Request.<br />
			 * <strong>Do not change this attribute.</strong>
			 */
			this.id = 0;
			/**
			 * The end tail of the url which will be request to the Server. It
			 * should be as same as the url-tail in the Server configuration.
			 */
			this.tail = "scomet";
			/**
			 * A number count which is to avoid using the url cached. (for
			 * Mricosoft Internet Exploere)
			 */
			this.count = 0;
			/**
			 * Get an object XMLHttpRequest from different Browsers if can.
			 */
			this.getAjaxRequest = function() {
				var xmlHttp;
				try {
					/** Opera, FireFox, Chrome, Safari, IE7+ */
					xmlHttp = new XMLHttpRequest();
				} catch (e) {
					try {
						/** IE5, IE6 */
						xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
					} catch (e) {
						return null;
					}
				}
				return xmlHttp;
			};
			/**
			 * Send a message to the Server.
			 */
			this.send = function(message) {
				this.ajax("message", "?id=" + this.id + "&message="
						+ this.formatText(encodeURI(message)) + "&count="
						+ ++this.count,
				/**
				 * This function will be called after a message request is being
				 * response. If the message returned is close command, this
				 * connection will be closed. If not, nothing will be done.
				 */
				function(message, conn) {
					if (message.length != 0 && message.charAt(0) == 'c') {
						conn.status = 0;
						conn.id = 0;
					}
				});
			};
			/**
			 * Link to the Server and a page id will be returned if success.
			 */
			this.link = function() {
				this.ajax("request", "?count=" + ++this.count,
				/**
				 * The id and the status of the connection will be changed, and
				 * a connection request will be held by Server.
				 */
				function(message, conn) {
					conn.id = parseInt(message.substring(2, message.length));
					conn.status = 1;
					conn.connection(conn);
				});
			};
			/**
			 * Close the connection.
			 */
			this.close = function() {
				this.status = 0;
				this.id = 0;
				this.ajax("close", "?id=" + this.id + "&count=" + ++this.count,
				/**
				 * Do nothing.
				 */
				function(message, conn) {
				});
			};
			/**
			 * the call back function.
			 */
			this.callBack = null;
			/**
			 * The connection request which will be held by Server. It will be
			 * returned when Server want to send messages to the Client. After
			 * that, a new connection request will be sent to Server.
			 */
			this.connection = function() {
				if (this.status == 0) {
					return;
				}
				this.ajax("connection", "?id=" + this.id + "&count="
						+ ++this.count,
				/**
				 * If the message returned is a close command, close this
				 * connection. If not, call the function callBack and send a new
				 * request.
				 */
				function(message, conn) {
					if (message.charAt(0) == 'c') {
						conn.status = 0;
						conn.id = 0;
					} else {
						conn.callBack(message.substring(2, message.length));
						conn.connection(conn);
					}
				});
			};
			/**
			 * Itself.
			 */
			this.self = this;
			/**
			 * This function encapsulates an Ajax request.<br />
			 * url: the relative path of this Ajax request.<br />
			 * attr: the attributes string what will be added to the url.<br />
			 * callBack: the function which will be called after the request is
			 * being returned.
			 */
			this.ajax = function(url, attr, callBack) {
				var ajaxRequest = this.getAjaxRequest();
				var self = this.self;
				ajaxRequest.onreadystatechange =
				/**
				 * call the function callBack to process the message.
				 */
				function() {
					if (ajaxRequest.readyState == 4) {
						callBack(ajaxRequest.responseText, self);
					}
				};
				ajaxRequest.open("GET", url + "." + this.tail + attr, true);
				ajaxRequest.send(null);
			};
			/**
			 * Change the symbol #,+,&,/ to /s,/p,/a,//.
			 */
			this.formatText = function(message) {
				return message.replace(/\//g, "//").replace(/#/gm, "/s")
						.replace(/\+/gm, "/p").replace(/&/gm, "/a");
			};
		};
	};
};